import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Scanner;

public class Main
{
	public static void main(final String... the_argv)
	{
		if (the_argv.length != 3)
		{
			System.out.println("Usage: java Program-Name.jar" +
					"<input file> <output file> <#timeCycles>"); // TODO:
																										// change
																										// Program-name
																										// later
			return;
		}

		String input_file = the_argv[0];
		String output_file = the_argv[1];
		int stopAfter = Integer.parseInt(the_argv[2]);
		
		Scanner threshold_reader = null;
		double checking_high_threshold = 0.0;
		double checking_low_threshold = 0.0;
		double debt_high_threshold = 0.0;
		double debt_low_threshold = 0.0;
		try
		{
			threshold_reader = new Scanner(new File("threshold.txt"));
		} catch (FileNotFoundException fnf)
		{
			fnf.printStackTrace();
		}
		
		if (threshold_reader != null)
		{
			threshold_reader.next();
			checking_high_threshold = threshold_reader.nextDouble();
			threshold_reader.next();
			checking_low_threshold = threshold_reader.nextDouble();
			threshold_reader.next();
			debt_high_threshold = threshold_reader.nextDouble();
			threshold_reader.next();
			debt_low_threshold = threshold_reader.nextDouble();
		}

		Scanner input_scanner = null;
		HashMap<Integer, Entry> entries = new HashMap<Integer, Entry>();
		try
		{
			input_scanner = new Scanner(new File(input_file));
		} catch (FileNotFoundException fnf)
		{
			fnf.printStackTrace();
		}

		if (input_scanner != null)
		{
			while (input_scanner.hasNextLine())
			{
				String entry = input_scanner.nextLine();

				if (entry.charAt(0) != '#')
				{
					entries.put(
							Integer.parseInt(entry.substring(0,
									entry.indexOf(";"))),
							new Entry(entry.substring(entry.indexOf(";") + 1,
									entry.length())));
				}
			}
		}
		File outputFile = new File(output_file);
		BufferedWriter output = null;
		try
		{
			output = new BufferedWriter(new FileWriter(outputFile));
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(entries.size() + " entries read.");
		// get the initial conditions
		Entry initialConditions = entries.get(0);
		// create a checking account and set most of its parameters
		Account checking = new CheckingAcnt();
		checking.setName("Checking");
		checking.setBalance(initialConditions.getChecking());
		checking.setHighThreshold(checking_high_threshold);
		checking.setLowThreshold(checking_low_threshold);

		Account savings = new SavingsAcnt();
		savings.setName("savings");
		savings.setBalance(initialConditions.getSaving());
		savings.setInterestRate(initialConditions.getSavingsInterestRate());

		Account debt = new DebtAcnt();
		debt.setName("Debt");
		debt.setBalance(initialConditions.getDebt());
		debt.setInterestRate(initialConditions.getDebtInterestRate());
		debt.setHighThreshold(debt_high_threshold);
		debt.setLowThreshold(debt_low_threshold);

		//
		debt.setInterestRecievingAccount(debt);

		debt.setUnderFlowAccount(checking);

		// interest from savings is deposited back into savings.
		savings.setInterestRecievingAccount(savings);

		// any surplus in checking goes to savings
		checking.setOverFlowAccount(savings);

		// if checking has insufficient funds, try to withraw from savings
		checking.setUnderFlowAccount(savings);

		// set up the sources and sinks
		Transaction wages = new Wages("Wages", checking,
				initialConditions.getIncomeDelta());
		Transaction expenses = new Expenses("Expenses", checking,
				initialConditions.getExpense());

		// report initial contitions to terminal
		System.out.println("InitialConditions " + initialConditions);

		// to keep track of time slices
		int timerTick = 1;

		// write header for output file
		try
		{
			output.write("Time Period,Wages,Expenses,Checking bal, Savings bal, Debt bal\n");
		} catch (IOException e1)
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// run the modeling engine
		while (stopAfter > timerTick - 1)
		{
			String comment = "";
			// check for changes in the conditions
			if (entries.containsKey(timerTick))
			{
				Entry newState = entries.get(timerTick);
				comment = newState.getComment();
				if (newState.getChecking() > 0 || newState.getChecking() < 0)
				{
					checking.setBalance(checking.getBalance()
							+ newState.getChecking());
					System.out.println("At timeTick " + timerTick
							+ " checking balance changed by "
							+ newState.getChecking() + " -- "
							+ newState.getComment());
				}
				if (newState.getSaving() > 0 || newState.getSaving() < 0)
				{
					savings.setBalance(savings.getBalance() + newState.getSaving());
					System.out.println("At timeTick " + timerTick
							+ " savings balance changed by "
							+ newState.getSaving() + " -- "
							+ newState.getComment());
				}
				if (newState.getDebt() > 0)
				{
					debt.withdraw(newState.getDebt());
					System.out.println("At timeTick " + timerTick
							+ " debt increased by " + newState.getDebt()
							+ " -- " + newState.getComment());
				}
				if (newState.getDebt() < 0)
				{
					debt.deposit(newState.getDebt());
					System.out.println("At timeTick " + timerTick
							+ " debt decreased by " + newState.getDebt()
							+ " -- " + newState.getComment());
				}
				if (newState.getIncomeDelta() > 0
						|| newState.getIncomeDelta() < 0)
				{
					wages.setRate(wages.getRate() + newState.getIncomeDelta());
					System.out.println("At timeTick " + timerTick
							+ " income changed by " + newState.getIncomeDelta()
							+ " -- " + newState.getComment());
				}
				if (newState.getExpense() > 0 || newState.getExpense() < 0)
				{
					expenses.setRate(expenses.getRate() + newState.getExpense());
					System.out.println("At timeTick " + timerTick
							+ " expenses changed by " + newState.getExpense()
							+ " -- " + newState.getComment());
				}
				if (newState.getSavingsInterestRate() != 0.0)
				{
					double rateAdj = newState.getSavingsInterestRate();
					savings.setInterestRate(savings.getInterestRate() + rateAdj);
					System.out.println("At timeTick " + timerTick
							+ " savings Interest changed by "
							+ newState.getSavingsInterestRate() + " -- "
							+ newState.getComment());
				}
				if (newState.getDebtInterestRate() != 0.0)
				{
					double rateAdj = newState.getDebtInterestRate();
					debt.setInterestRate(debt.getInterestRate() + rateAdj);
					System.out.println("At timeTick " + timerTick
							+ " debt Interest changed by "
							+ newState.getDebtInterestRate() + " -- "
							+ newState.getComment());
				}

			}

			// tell wages to provide income
			wages.doTimeTick();

			// pay bills
			try
			{
				expenses.doTimeTick();
			} catch (IllegalStateException e)
			{
				System.out.println(e.getMessage());
			}

			// pay debt
			try
			{
				debt.doTimeTick();

			} catch (IllegalStateException e)
			{
				System.out.println(e.getMessage());
			}
			// accounts need a doTimeTick to calculate interest and transfer
			// funds
			try
			{
				checking.doTimeTick();
			} catch (IllegalStateException e)
			{
				System.out.println("checking cannot maintain minimum balance");
			}
			savings.doTimeTick();

			System.out.println(timerTick + " " + checking.getBalance() + " "
					+ savings.getBalance() + " " + debt.getBalance());

			// write current state to output file
			if (output != null)
			{
				// write state to output file
				String s = timerTick + "," + wages.getRate() + ","
						+ expenses.getRate() + "," + checking.getBalance()
						+ "," + savings.getBalance() + "," + debt.getBalance()
						+ "," + comment;
				try
				{
					output.write(s + "\n");
				} catch (IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			timerTick++;
		}
		// close the output file
		try
		{
			output.close();
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// print a summary to terminal
		System.out.println("Net worth after " + stopAfter + " cycles:");
		System.out.println("Checking ="
				+ NumberFormat.getCurrencyInstance().format(
						checking.getBalance()));
		System.out.println("Savings ="
				+ NumberFormat.getCurrencyInstance().format(
						savings.getBalance()));
		System.out.println("Debt ="
				+ NumberFormat.getCurrencyInstance().format(debt.getBalance()));
		System.out.println("Total ="
				+ NumberFormat.getCurrencyInstance().format(
						checking.getBalance() + savings.getBalance()
								- debt.getBalance()));

	}// end main
} // end class

